﻿using FieldTools.Util;
using Namotion.Reflection;
using Serilog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FieldTools.Client.Model.Base
{
    /// <summary>
    /// 基础模型，数据模型继承此类可更方便完成数据通知及缓存本地化，推荐使用nameof传入属性名
    /// <para> set中调用SavaConfigAndRaisePropertyChanged可自动保存数据模型的配置并通知</para> 
    /// <para> set中调用RaisePropertyChanged可仅通知</para>
    /// <para> 注意：</para>
    /// <para> 1、数据模型默认初始化的时候，请使用值初始化，而非属性，否则会导致数据数据被重置</para>
    /// <para> 2、数据模型具有唯一性，在任何地方的每次修改属性，若属性没被标记为[NoSave]均会修改到本地覆盖之前的数据，因此，非必要情况下，不要进行额外的数据模型的初始化和修改</para>
    /// </summary>
    public class BaseModel
        : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 发起通知
        /// </summary>
        /// <param name="propertyName">属性名</param>
        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        readonly string _className = "";
        /// <summary>
        /// 带保存对象
        /// </summary>
        /// <param name="className"></param>
        public BaseModel(string className)
        {
            _className = className;
        }
        /// <summary>
        /// 不带保存对象，无法使用SavaConfig相关内容
        /// </summary>
        public BaseModel()
        {

        }
        /// <summary>
        /// 保存配置，目的是保存单个属性，实际是保存整个实体类
        /// </summary>
        public void SavaConfig(object obj, string propertyName)
        {
            //var className = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;
            if(string.IsNullOrEmpty(_className))return;
            try
            {
                if (obj != null)
                {
                    CacheHelper.SetCache(_className, obj);
                    //var val = obj.GetPropertyValue(propertyName);
                    //MessageBox.Show("保存成功");
                    //Log.Information(_className+"."+ propertyName + "保存成功，保存内容为：" + val);
                }
                else
                {
                    //MessageBox.Show("配置文件异常");
                    Log.Information(_className + "配置文件异常");
                }
            }
            catch (Exception ex)
            {
                //MessageBox.Show("保存失败" + ex);
                Log.Information(_className + "保存失败，" + ex);
            }
        }

        /// <summary>
        /// 保存和通知属性变化
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        public void SavaConfigAndRaisePropertyChanged(object obj, string propertyName, object value)
        {
            if(string.IsNullOrEmpty(_className))return;
            //1、由于原属性值已被覆盖，这里的取旧值需要从缓存中获取，需要避免在其他地方使用，防止异常，此处采用自定义属性的方式，标记具备某属性的类可调用（且限制派生继承），而其他的类不可调用
            //2、采用反射获取属性值是会调用get，相对应的设置属性值是使用的set，需要注意的是，若set方法使用了该方法，那该方法一定不要使用反射来设置属性值，否则会陷入无限循环，导致栈溢出，可采取拆箱后重新封箱的手段来进行值的设置
            var oldObjJson = CacheHelper.GetCache(_className).ToString();
            var oldValueUb = oldObjJson?.ToObject<object>().ObjectUnboxing();

            //判断是否可保存、是否为无需保存属性、判断数据是否发生变化
            if (obj.HasAttribute(nameof(SaveAttribute)) && !obj.HasAttribute(propertyName, nameof(NotSaveAttribute)) && (oldValueUb == null || oldValueUb[propertyName] == null || !oldValueUb[propertyName].Equals(value)))
            {
                //if (oldValueUb != null)
                //{
                //    if (!oldValueUb.TryAdd(propertyName, value))
                //    {
                //        oldValueUb[propertyName] = value;
                //    }
                //    object objTemp = oldValueUb.ToObject();
                //    SavaConfig(objTemp, propertyName);
                //}
                //else
                //{
                    SavaConfig(obj, propertyName);
                //}
            }
            this.RaisePropertyChanged(propertyName);
        }
    }
}
